/*
 * Copyright (C) 2008, Velocityme Partnership
 * SprintSessionBean.java
 *
 * Created on 27 August 2008, 19:49
 */

package com.velocityme.session;

import com.velocityme.entity.ChangeDeltaItemTypeBean;
import com.velocityme.entity.ChangeDeltaTypeBean;
import com.velocityme.entity.PermissionBean;
import javax.ejb.*;
import javax.naming.*;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

import com.velocityme.interfaces.*;
import com.velocityme.utility.DurationRemaining;
import com.velocityme.utility.InvalidKeyException;
import com.velocityme.utility.PermissionDeniedException;
import com.velocityme.utility.TimeEntry;
import com.velocityme.valueobjects.*;
import java.rmi.RemoteException;

/**
 *
 * @author  Robert Crida Work
 * @ejb.bean
 *           type="Stateless"
 *           cmp-version="2.x"
 *           name="SprintSession"
 *           jndi-name="ejb/SprintSession"
 *           view-type="local"
 *           transaction-type="Container"
 * @ejb.transaction type="Required"
 *
 * @ejb.util generate="physical"
 */
public class SprintSessionBean implements SessionBean {
    
    private SessionContext m_context;

    private DateFormat m_formatter;

    private SearchSessionLocal m_searchSessionLocal;

    private PermissionLocal m_permissionSprintCreate;
    private PermissionLocal m_permissionSprintEdit;
    private PermissionLocal m_permissionNodeView;

    /**
     * If the user has permission to create a new Sprint in the specified parent
     * node then go ahead and create it and log an appropriate ChangeDelta.
     *
     * @param p_key User's security key
     * @param p_parentNode The node which will contain the new Role
     * @param p_sprintValue The value details for the Sprint part of the sprint
     *
     * @ejb.interface-method view-type="local"
     **/
    public SprintLocal createSprint(KeySessionLocal p_keyLocal,
                                 NodeLocal p_parentNodeLocal,
                                 com.velocityme.valueobjects.SprintValue p_sprintValue,
                                 Collection p_personsLocal)
        throws PermissionDeniedException, InvalidKeyException {
        try {
            if (m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, p_parentNodeLocal).contains(m_permissionSprintCreate)) {
                p_sprintValue.getNodeValue().setType("Sprint");
                p_sprintValue.getNodeValue().setFinalType("Sprint");
                DBObjectLocal dBObjectLocal = DBObjectUtil.getLocalHome().create();
                NodeValue nodeValue = p_sprintValue.getNodeValue();
                NodeLocal nodeLocal = NodeUtil.getLocalHome().create(dBObjectLocal, nodeValue, p_parentNodeLocal);
                nodeValue.setNodeId(nodeLocal.getNodeId());
                SprintLocal sprintLocal = SprintUtil.getLocalHome().create(nodeLocal, p_sprintValue, p_personsLocal);
                ChangeDeltaLocal changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.CREATE, p_keyLocal.getUserLocal(), dBObjectLocal);
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.NAME, nodeValue.getName());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.DESCRIPTION, nodeValue.getDescription());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.START_TIME, m_formatter.format(p_sprintValue.getStartDate()));
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.STOP_TIME, m_formatter.format(p_sprintValue.getStopDate()));
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.SCRUM_MASTER, sprintLocal.getScrumMasterLocal().getName());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.PARENT_NODE, p_parentNodeLocal.getPathName());
                return sprintLocal;
            }
            else
                throw new PermissionDeniedException();
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(CreateException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * If the user has permission to edit the Sprint then do so and log the
     * changes with an appropriate ChangeDelta.
     *
     * @param p_key User's security key
     * @param p_role The Role object being editted
     * @param p_roleValue The value details for the Role part of the role
     *
     * @ejb.interface-method view-type="local"
     **/
    public void editSprint(KeySessionLocal p_keyLocal,
                           SprintLocal p_sprintLocal,
                           com.velocityme.valueobjects.SprintValue p_sprintValue,
                           Collection p_personsLocal)
        throws PermissionDeniedException, InvalidKeyException {
        try {
            NodeLocal nodeLocal = p_sprintLocal.getNodeLocal();
            if (m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, nodeLocal).contains(m_permissionSprintEdit)) {
                NodeValue nodeValue = p_sprintValue.getNodeValue();
                ChangeDeltaLocal changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.EDIT, p_keyLocal.getUserLocal(), nodeLocal.getDBObjectLocal());
                if (!nodeValue.getName().equals(nodeLocal.getName()))
                    changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.NAME, nodeValue.getName());
                if (!nodeValue.getDescription().equals(nodeLocal.getDescription()))
                    changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.DESCRIPTION, nodeValue.getDescription());
                if (!p_sprintLocal.getStartDate().equals(p_sprintValue.getStartDate()))
                    changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.START_TIME, m_formatter.format(p_sprintValue.getStartDate()));
                if (!p_sprintLocal.getStopDate().equals(p_sprintValue.getStopDate()))
                    changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.STOP_TIME, m_formatter.format(p_sprintValue.getStopDate()));
                Integer oldScrumMasterId = p_sprintLocal.getScrumMasterLocal().getPersonId();
                nodeLocal.setNodeValue(nodeValue);
                p_sprintLocal.setSprintValue(p_sprintValue);
                if (!p_sprintLocal.getScrumMasterLocal().getPersonId().equals(oldScrumMasterId))
                    changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.SCRUM_MASTER, p_sprintLocal.getScrumMasterLocal().getName());
                p_sprintLocal.setTeamPersonsLocal(p_personsLocal);
            }
            else
                throw new PermissionDeniedException();
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(CreateException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * If the user has permission to access a sprint then return the local object.
     *
     * @param p_key User's security key
     * @param p_sprintPK The Sprint object to lookup
     *
     * @ejb.interface-method view-type="local"
     **/
    public SprintLocal getSprintLocal(KeySessionLocal p_keyLocal, 
                                      SprintPK p_sprintPK)
        throws PermissionDeniedException, InvalidKeyException {
        try {
            SprintLocal sprintLocal = SprintUtil.getLocalHome().findByPrimaryKey(p_sprintPK);
            NodeLocal nodeLocal = sprintLocal.getNodeLocal();
            if (m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, nodeLocal).contains(m_permissionNodeView)) {
                return sprintLocal;
            }
            else
                throw new PermissionDeniedException();
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(FinderException e) {
            throw new EJBException(e);
        }
    }
    
    private void addSearch(java.util.Map p_map, java.util.Map p_search) {
        if (p_map.isEmpty()) {
            p_map.putAll(p_search);
        }
        else {
            Iterator j = p_search.entrySet().iterator();
            while (j.hasNext()) {
                Map.Entry entry = (Map.Entry)j.next();
                DurationRemaining dr = (DurationRemaining)p_map.get(entry.getKey());
                dr.add((DurationRemaining)entry.getValue());
            }
        }
    }
    
    /**
     * Get the durations for burndown chart.
     *
     * @param p_key User's security key
     * @param p_sprintPK The Sprint object to lookup
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Map searchDurationSum(KeySessionLocal p_keyLocal,
                                           SprintLocal p_sprintLocal) 
        throws PermissionDeniedException, InvalidKeyException {
        try {
            Iterator i = p_sprintLocal.getNodeLocal().getAllLinkedNodesLocal().iterator();
            Map results = new TreeMap();
            Date start = p_sprintLocal.getStartDate();
            Date stop = new Date();
            // get the earlier of now or the end of sprint
            if (stop.after(p_sprintLocal.getStopDate()))
                stop = p_sprintLocal.getStopDate();
            NodeSessionLocal ns = NodeSessionUtil.getLocalHome().create();
            while (i.hasNext()) {
                NodeLocal nodeLocal = (NodeLocal)i.next();
                addSearch(results, ns.searchDurationSum(p_keyLocal, nodeLocal, false, true, start, stop, false));
            }
            addSearch(results, ns.searchUnplannedSum(p_keyLocal, p_sprintLocal.getTeamPersonsLocal(), true, start, stop, false));
            return results;
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(CreateException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * Get the durations for a progress chart.
     * 
     * @param p_key User's security key
     * @param p_sprintLocal The Sprint object
     * 
     * @ejb.interface-method view-type="local"
     */
    public java.util.Map searchProgressSum(KeySessionLocal p_keyLocal,
                                           SprintLocal p_sprintLocal)
            throws PermissionDeniedException, InvalidKeyException {
        try {
            Iterator i = p_sprintLocal.getNodeLocal().getAllLinkedNodesLocal().iterator();
            Map results = new TreeMap(new Comparator() {
                public int compare(Object o1, Object o2) {
                    try {
                        TaskValue v1 = (TaskValue)o1;
                        TaskLocal t1 = TaskUtil.getLocalHome().findByPrimaryKey(v1.getPrimaryKey());
                        String n1 = t1.getProjectPathName() + t1.getNodeLocal().getName();
                        TaskValue v2 = (TaskValue)o2;
                        TaskLocal t2 = TaskUtil.getLocalHome().findByPrimaryKey(v2.getPrimaryKey());
                        String n2 = t2.getProjectPathName() + t2.getNodeLocal().getName();
                        return n1.compareTo(n2);
                    } catch (Exception ex) {
                        return 0;
                    }
                }
            });
            Date start = p_sprintLocal.getStartDate();
            Date stop = p_sprintLocal.getStopDate();
            NodeSessionLocal ns = NodeSessionUtil.getLocalHome().create();
            TaskSessionLocal ts = TaskSessionUtil.getLocalHome().create();
            while (i.hasNext()) {
                NodeLocal nodeLocal = (NodeLocal)i.next();
                if (nodeLocal.getTaskLocal() != null) {
                    TaskLocal taskLocal = nodeLocal.getTaskLocal();
                    DurationRemaining dr = (DurationRemaining)ns.searchDurationSum(p_keyLocal, nodeLocal, true, true, start, stop, true).get("-");
                    results.put(taskLocal.getTaskValue(), dr);
                }
            }
            return results;
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(CreateException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * Create the Session Bean.
     * @throws CreateException 
     * @ejb:create-method view-type="remote"
     */
    public void ejbCreate() throws CreateException {
        m_formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm");
        try {
            m_searchSessionLocal = SearchSessionUtil.getLocalHome().create();
            m_permissionSprintCreate = PermissionUtil.getLocalHome().findByPrimaryKey(new PermissionPK(new Integer(PermissionBean.SPRINT_CREATE)));
            m_permissionSprintEdit = PermissionUtil.getLocalHome().findByPrimaryKey(new PermissionPK(new Integer(PermissionBean.SPRINT_EDIT)));
            m_permissionNodeView = PermissionUtil.getLocalHome().findByPrimaryKey(new PermissionPK(new Integer(PermissionBean.NODE_VIEW)));
        }
        catch(NamingException e) {
            throw new CreateException(e.getMessage());
        }
        catch(FinderException e) {
            throw new CreateException(e.getMessage());
        }
    }

    public void ejbActivate() throws java.rmi.RemoteException {
    }
    
    public void ejbPassivate() throws java.rmi.RemoteException {
    }
    
    public void ejbRemove() throws java.rmi.RemoteException {
    }
    
    public void setSessionContext(javax.ejb.SessionContext sessionContext) throws java.rmi.RemoteException {
        m_context = sessionContext;
    }
    
}
